package ca.ualberta.cs.CLRServer.Interface;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import javax.ws.rs.WebApplicationException;

import ca.ualberta.cs.CLRServer.resources.JAXBBean;

public class SecureResource {

	/**
	 * Checks the format and size of the url given in the request being processed
	 * @param url
	 */
	protected void checkUrl(String url){
		if(url.length() > 1000)
			throw new WebApplicationException(414);
		try{
			new URI(url);			
		} catch (URISyntaxException e) {
			throw new WebApplicationException(415);
		}		
		checkScript(url);
	}
	
	/**
	 * Checks that the coming request (List<JAXBBean>) doesn't have an unusual size (buffer flow - attack)
	 * The unusual size is considered greater than the given maxLimit. If maxLimit is not known, another method with default value of 15 is called {@link "protected void checkBufferOverflow(List<JAXBBean> requestArr)"}
	 * @param requestArr
	 * @param maxLimit
	 */
	protected void checkBufferOverflow(List<JAXBBean> requestArr, int maxLimit){
		if(requestArr.size() > maxLimit)
			throw new WebApplicationException(413);
	}
	
	/**
	 * Checks that the coming request (List<JAXBBean>) doesn't have an unusual size (buffer flow - attack)
	 * The normal size according to the designated client (CoRAL fab4browser) should not be more than 15
	 * @param requestArr
	 */
	protected void checkBufferOverflow(List<JAXBBean> requestArr){
		if(requestArr.size() > 15)
			throw new WebApplicationException(413);
	}
	
	/**
	 * Makes sure that all required fields are contained in the request : makes sure that the request is what is expected
	 * @param requestArr
	 * @param keys : the required keys to handle a request
	 */
	protected void checkRequiredKeys(List<JAXBBean> requestArr, String... keys){
		for(String key : keys){
			boolean exists = false;
			for(JAXBBean bean : requestArr){
				if(bean.getKey() == null)
					continue;
				if(bean.getKey().equals(key)){
					exists = true;
					break; //look for next key
				}
			}
			if( !exists ) //if one key does not exist
				throw new WebApplicationException(415);
		}
		
		for(JAXBBean bean : requestArr){
			if(bean.getVal() != null)
				checkScript(bean.getVal());
			if(bean.getKey() != null)
				checkScript(bean.getKey());			
		}
	}
	
	protected String checkScript(String val){
		/*if(val.contains("<script") || val.contains("</script>")
				|| val.contains("< script")){*/
		val.replace("<script", "&lt;script");
		val.replace("</script", "&lt;/script");
		val.replace("< script", "&lt; script");
		val.replace("script>", "script&gt;");
		val.replace("script >", "script &gt;");
		return val;
//			throw new WebApplicationException(415);
	}
	
	/**
	 * String is the username
	 */
	static HashMap<PostContent,HashMap<String,Session>> limitationData = new HashMap<PostContent,HashMap<String,Session>>();
	
	protected static void checkPostLimit(String username, PostContent content){
		if(!limitationData.containsKey(content))
			limitationData.put(content, new HashMap<String, Session>());
		if(!limitationData.get(content).containsKey(username))
			limitationData.get(content).put(username, new Session(content.getTimelimit(), content.getNumLimit()));
		else if(limitationData.get(content).get(username).limitAttempts()){
			throw new WebApplicationException(413); //limit this attempt
		}
	}
}

class Session{
	Date firstAttempt; //in milliseconds
	int numAttempts;
	int timelimit; //in seconds
	int numlimit;
	
	public Session(int tlimit, int nlimit){
		firstAttempt = new Date();
		numAttempts = 1;
		timelimit = tlimit;
		numlimit = nlimit;
	}
	
	public void reset(){
		firstAttempt = new Date();
		numAttempts = 1;
	}
	
	/**
	 * called when an attempt is made, num of attempts is increased, then it checks whether this attempt
	 * should be allowed or not, considering how many attempts this person has made in the timelimit
	 * @return true if it should limit (restrict) this last attempt. return false if it is ok.
	 */
	public boolean limitAttempts(){
		numAttempts ++;
		long now = (new Date()).getTime();
		if( now - firstAttempt.getTime() > timelimit*1000 ){
			firstAttempt = new Date();
			numAttempts = 1;
		}
		else if(numAttempts > numlimit){
			return true;
		}
		return false;
	}
	
}